#ifndef JMESSAGE_H
#define JMESSAGE_H
/*
	对象通讯

	class A //被观察者
	{
		J_MESSAGE(msg1) //消息
		J_MESSAGE(msg2) //可以有多个种类消息
		J_MESSAGE(msg3, int) //消息可以有参数（可变模板），收消息方参数必须与之对应
	}

	class Observer //观察者
	{
		void slot1() {}
		void slot2(int a) {}
	};


	A a;
	Observer b;

	a.msg1.subscribe(&b, &Observer::slot); //订阅消息（可以一对多）
	a.msg3.subscribe(&b, &Observer::slot2);

	a.msg1.send(); //发送消息
	a.msg2.send();
	a.msg3.send();

*/
#include <JXQCore/JXQGlobal>
#include <JXQCore/JDebug>
#include <JXQCore/JMutex>
#include <functional>
#include <atomic>
#include <list>

namespace JXQ
{

#define J_SLOT  void

template <typename ...Args>
class FuncSet;

template <typename ...Args>
class JMessage
{
public:
	JMessage();
	~JMessage();

public:
	void subscribe(J_SLOT(*callBack)(Args...));

	template <typename Class>
	void subscribe(const Class *obj, J_SLOT(Class::*callBack)(Args...)); //支持虚函数

	template <typename Func>
	void subscribe(Func callBackLambda);

public:
	void unsubscribe(J_SLOT(*callBack)(Args...));

	template <typename Class>
	void unsubscribe(const Class *obj, J_SLOT(Class::*callBack)(Args...));

	template <typename Class>
	void unsubscribe(const Class *obj);

	template <typename Class>
	void unsubscribe(J_SLOT(Class::*callBack)(Args...));

	void unsubscribeAll();

public:
	void send(Args...);
	void blockMessage(bool flag = true); //设置true则阻止发送消息
	bool messageBlocked() const;

public:
	JMessage(const JMessage &other);
	void operator=(const JMessage &other);

private:
	typedef std::function<J_SLOT(Args...)>  Package;

private:
	template <typename Class, typename Func>
	Package bind(Class *obj, Func func);

	template <typename Class, typename Func, typename T0>
	Package bind(Class *obj, Func func);

	template <typename Class, typename Func, typename T0, typename T1>
	Package bind(Class *obj, Func func);

	template <typename Class, typename Func, typename T0, typename T1, typename T2>
	Package bind(Class *obj, Func func);

	template <typename Class, typename Func, typename T0, typename T1, typename T2, typename T3>
	Package bind(Class *obj, Func func);

	template <typename Class, typename Func, typename T0, typename T1, typename T2, typename T3, typename T4>
	Package bind(Class *obj, Func func);

	template <typename Class, typename Func, typename T0, typename T1, typename T2, typename T3, typename T4, typename T5>
	Package bind(Class *obj, Func func);

	template <typename Class, typename Func, typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
	Package bind(Class *obj, Func func);

	template <typename Class, typename Func, typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
	Package bind(Class *obj, Func func);

	template <typename Class, typename Func, typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
	Package bind(Class *obj, Func func);

	template <typename Class, typename Func, typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9>
	Package bind(Class *obj, Func func);

	template <typename Class, typename Func, typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10>
	Package bind(Class *obj, Func func);

	template <typename Class, typename Func, typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10, typename T11>
	Package bind(Class *obj, Func func);

private:
	FuncSet<Args...> *m_funcSet;
};

struct CallBackKey
{
	const void *obj;
	AddressBitsWide func;

	explicit inline CallBackKey(AddressBitsWide func, const void *obj = nullptr) :
		obj(obj), func(func) {}

	inline bool operator==(const CallBackKey &other) const
	{ return obj == other.obj and func == other.func; }
};

template <typename ...Args>
class FuncSet
{
public:
	~FuncSet();

public:
	typedef std::function<J_SLOT(Args...)>  Package;
	void emplace(const CallBackKey &key, Package &&package);

public:
	void remove(AddressBitsWide func, const void *obj = nullptr);
	void removeObject(const void *obj);
	void removeClassMothed(AddressBitsWide func);
	void clear();

public:
	void send(Args&&... args);
	void blockMessage(bool flag);
	bool messageBlocked() const;

public:
	std::atomic_uint m_ref {1};

private:
	typedef std::pair<CallBackKey, Package>  Node;
	std::list<Node> m_UList;
	std::mutex m_mutex;
	std::atomic_bool m_block {false};
};

#define J_MESSAGE(msgName, ...) \
	public: JXQ::JMessage<__VA_ARGS__>  msgName;

template<typename... Args>
inline FuncSet<Args...>::~FuncSet()
{
	m_mutex.lock();
	m_UList.clear();
	m_mutex.unlock();
}

template<typename... Args>
inline void FuncSet<Args...>::emplace(const CallBackKey &key, Package &&package)
{
	J_MUTEX_LOCKER(m_mutex);

	if( key.obj == 0 and key.func == 0 )
		m_UList.emplace_back(key, package);
	else
	{
		for(auto &pair : m_UList)
		{
			if( pair.first == key )
			{
				jDebug() << "Message duplicate connection (automatically ignored)";
				return ;
			}
		}
		m_UList.emplace_back(key, package);
	}
}

template<typename... Args>
void FuncSet<Args...>::remove(AddressBitsWide func, const void *obj)
{
	J_MUTEX_LOCKER(m_mutex);
	auto it = m_UList.begin();

	for(; it!=m_UList.end(); ++it)
	{
		if( it->first.obj == obj and it->first.func == func )
		{
			m_UList.erase(it);
			break;
		}
	}
}

template<typename... Args>
void FuncSet<Args...>::removeObject(const void *obj)
{
	J_MUTEX_LOCKER(m_mutex);
	auto it = m_UList.begin();

	for(; it!=m_UList.end(); ++it)
	{
		if( it->first.obj == obj )
			m_UList.erase(it);
	}
}

template<typename... Args>
void FuncSet<Args...>::removeClassMothed(AddressBitsWide func)
{
	J_MUTEX_LOCKER(m_mutex);
	auto it = m_UList.begin();

	for(; it!=m_UList.end(); ++it)
	{
		if( it->first.func == func )
			m_UList.erase(it);
	}
}

template<typename... Args>
inline void FuncSet<Args...>::clear()
{
	J_MUTEX_LOCKER(m_mutex);
	m_UList.clear();
}

template<typename... Args>
void FuncSet<Args...>::send(Args&&... args)
{
	if( m_block	)
		return ;

	m_mutex.lock();

	for(auto &pair : m_UList)
	{
		if( m_ref - 1 == 0 )
			return ;

		pair.second(std::forward<Args>(args)...);
		m_mutex.unlock();

		if( m_ref - 1 == 0 )
			return ;

		m_mutex.lock();
	}

	m_mutex.unlock();
}

template<typename... Args>
void FuncSet<Args...>::blockMessage(bool flag)
{
	m_block = flag;
}

template<typename... Args>
bool FuncSet<Args...>::messageBlocked() const
{
	return m_block;
}

template <typename ...Args>
inline JMessage<Args...>::JMessage() :
	m_funcSet(new FuncSet<Args...>())
{

}

template <typename ...Args>
inline JMessage<Args...>::~JMessage()
{
	if( not --m_funcSet->m_ref )
		delete m_funcSet;
}

template <typename ...Args>
inline void JMessage<Args...>::subscribe(J_SLOT(*callBack)(Args...))
{
	AddrTlr<J_SLOT(*)(Args...)> at(callBack);
	m_funcSet->emplace(CallBackKey(at.addr), Package(callBack));
}

template <typename ...Args>
template <typename Class>
inline void JMessage<Args...>::subscribe(const Class *obj, J_SLOT(Class::*callBack)(Args...))
{
	AddrTlr<void(Class::*)(Args...)> at(callBack);

	typedef J_SLOT(Class::*Method)(Args...);
	m_funcSet->emplace(CallBackKey(at.addr, obj), this->bind<Class, Method, Args...>(J_CCT(Class*,obj), callBack));
}

template <typename ...Args>
template <typename Func>
inline void JMessage<Args...>::subscribe(Func callBackLambda)
{
	m_funcSet->emplace(CallBackKey(0), Package(callBackLambda));
}

template <typename ...Args>
inline void JMessage<Args...>::unsubscribe(void(*callBack)(Args...))
{
	AddrTlr<J_SLOT(*)(Args...)> at(callBack);
	m_funcSet->remove(at.addr);
}

template <typename ...Args>
template <typename Class>
inline void JMessage<Args...>::unsubscribe(const Class *obj, J_SLOT(Class::*callBack)(Args...))
{
	AddrTlr<void(Class::*)(Args...)> at(callBack);
	m_funcSet->remove(at.addr, obj);
}

template <typename ...Args>
template <typename Class>
inline void JMessage<Args...>::unsubscribe(const Class *obj)
{
	m_funcSet->removeObject(obj);
}

template <typename ...Args>
template <typename Class>
inline void JMessage<Args...>::unsubscribe(J_SLOT(Class::*callBack)(Args...))
{
	AddrTlr<void(Class::*)(Args...)> at(callBack);
	m_funcSet->removeObject(at.addr);
}

template <typename ...Args>
inline void JMessage<Args...>::unsubscribeAll()
{
	m_funcSet->clear();
}

template <typename ...Args>
void JMessage<Args...>::send(Args...args)
{
	if( ++m_funcSet->m_ref == 1 )
		delete m_funcSet;
	else
	{
		m_funcSet->send(std::forward<Args>(args)...);
		if( not --m_funcSet->m_ref )
			delete m_funcSet;
	}
}

template <typename ...Args>
inline void JMessage<Args...>::blockMessage(bool flag)
{
	m_funcSet->blockMessage(flag);
}

template <typename ...Args>
inline bool JMessage<Args...>::messageBlocked() const
{
	return m_funcSet->messageBlocked();
}

template <typename ...Args>
inline JMessage<Args...>::JMessage(const JMessage &other) :
	m_funcSet(new FuncSet<Args...>())
{

}

template <typename ...Args>
template <typename Class, typename Func>
inline typename JMessage<Args...>::Package JMessage<Args...>::bind(Class *obj, Func func)
{
	return std::bind(func, obj);
}

template <typename ...Args>
template <typename Class, typename Func, typename T0>
inline typename JMessage<Args...>::Package JMessage<Args...>::bind(Class *obj, Func func)
{
	return std::bind(func, obj, std::placeholders::_1);
}

template <typename ...Args>
template <typename Class, typename Func, typename T0, typename T1>
inline typename JMessage<Args...>::Package JMessage<Args...>::bind(Class *obj, Func func)
{
	return std::bind(func, obj, std::placeholders::_1, std::placeholders::_2);
}

template <typename ...Args>
template <typename Class, typename Func, typename T0, typename T1, typename T2>
inline typename JMessage<Args...>::Package JMessage<Args...>::bind(Class *obj, Func func)
{
	return std::bind(func, obj, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
}

template <typename ...Args>
template <typename Class, typename Func, typename T0, typename T1, typename T2, typename T3>
inline typename JMessage<Args...>::Package JMessage<Args...>::bind(Class *obj, Func func)
{
	return std::bind(func, obj, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);
}

template <typename ...Args>
template <typename Class, typename Func, typename T0, typename T1, typename T2, typename T3, typename T4>
inline typename JMessage<Args...>::Package JMessage<Args...>::bind(Class *obj, Func func)
{
	return std::bind(func, obj, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
					 std::placeholders::_4, std::placeholders::_5);
}

template <typename ...Args>
template <typename Class, typename Func, typename T0, typename T1, typename T2, typename T3, typename T4, typename T5>
inline typename JMessage<Args...>::Package JMessage<Args...>::bind(Class *obj, Func func)
{
	return std::bind(func, obj, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
					 std::placeholders::_4, std::placeholders::_5, std::placeholders::_6);
}

template <typename ...Args>
template <typename Class, typename Func, typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
inline typename JMessage<Args...>::Package JMessage<Args...>::bind(Class *obj, Func func)
{
	return std::bind(func, obj, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4,
					 std::placeholders::_5, std::placeholders::_6, std::placeholders::_7);
}

template <typename ...Args>
template <typename Class, typename Func, typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
inline typename JMessage<Args...>::Package JMessage<Args...>::bind(Class *obj, Func func)
{
	return std::bind(func, obj, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4,
					 std::placeholders::_5, std::placeholders::_6, std::placeholders::_7, std::placeholders::_8);
}

template <typename ...Args>
template <typename Class, typename Func, typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
inline typename JMessage<Args...>::Package JMessage<Args...>::bind(Class *obj, Func func)
{
	return std::bind(func, obj, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5,
					 std::placeholders::_6, std::placeholders::_7, std::placeholders::_8, std::placeholders::_9);
}

template <typename ...Args>
template <typename Class, typename Func, typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9>
inline typename JMessage<Args...>::Package JMessage<Args...>::bind(Class *obj, Func func)
{
	return std::bind(func, obj, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5,
					 std::placeholders::_6, std::placeholders::_7, std::placeholders::_8, std::placeholders::_9, std::placeholders::_10);
}

template <typename ...Args>
template <typename Class, typename Func, typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10>
inline typename JMessage<Args...>::Package JMessage<Args...>::bind(Class *obj, Func func)
{
	return std::bind(func, obj, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6,
					 std::placeholders::_7, std::placeholders::_8, std::placeholders::_9, std::placeholders::_10, std::placeholders::_11);
}

template <typename ...Args>
template <typename Class, typename Func, typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10, typename T11>
inline typename JMessage<Args...>::Package JMessage<Args...>::bind(Class *obj, Func func)
{
	return std::bind(func, obj, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6,
					 std::placeholders::_7, std::placeholders::_8, std::placeholders::_9, std::placeholders::_10, std::placeholders::_11, std::placeholders::_12);
}

} //namespace JXQ


#endif //JMESSAGE_H
